package org.carpediem.conman.rest;

import org.carpediem.conman.entity.*;
import org.carpediem.schema.conman.*;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.Providers;
import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

/**
 * @author Mark Petrovic
 */
@Provider
@Produces(MediaType.APPLICATION_XML)
public class DomainObjectProvider extends RestBase implements MessageBodyWriter<Object> {
    private final static Logger logger = Logger.getLogger(DomainObjectProvider.class.getName());

    private final Providers providers;

    public DomainObjectProvider(@Context Providers providers, @Context UriInfo uriInfo) throws JAXBException {
        super(uriInfo);
        this.providers = providers;
    }

    public boolean isWriteable(Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
        return DomainObject.class.isAssignableFrom(aClass) || DomainObjectBag.class.isAssignableFrom(aClass);
    }

    public long getSize(Object o, Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    public void writeTo(Object entity, Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType,
                        MultivaluedMap<String, Object> headers, OutputStream outputStream) throws IOException, WebApplicationException {
        if (entity == null) {
            logger.warning("Entity is null");
            return;
        }
        if (entity instanceof Contact) {
            ContactInfo cInfo = contactToContactInfo((Contact) entity);
            MessageBodyWriter<ContactInfo> p = providers.getMessageBodyWriter(ContactInfo.class, aClass, annotations, mediaType);
            p.writeTo(cInfo, ContactInfo.class, ContactInfo.class, annotations, mediaType, headers, outputStream);
        } else if (entity instanceof ContactBag) {
            ContactBag bag = (ContactBag) entity;
            ContactInfos cInfos = new ContactInfos();
            List<BContactInfo> bInfos = cInfos.getContact();
            for (Contact contact : bag) {
                BContactInfo bInfo = contactToBContactInfo(contact);
                bInfos.add(bInfo);
            }

            MessageBodyWriter<ContactInfos> p = providers.getMessageBodyWriter(ContactInfos.class, aClass, annotations, mediaType);
            p.writeTo(cInfos, ContactInfos.class, ContactInfos.class, annotations, mediaType, headers, outputStream);
        } else if (entity instanceof Document) {
            XDocumentInfo dInfo = documentToXDocumentInfo((Document) entity);
            MessageBodyWriter<XDocumentInfo> p = providers.getMessageBodyWriter(XDocumentInfo.class, aClass, annotations, mediaType);
            p.writeTo(dInfo, XDocumentInfo.class, XDocumentInfo.class, annotations, mediaType, headers, outputStream);
        } else if (entity instanceof DocumentBag) {
            DocumentBag bag = (DocumentBag) entity;
            DocumentInfos documentInfos = new DocumentInfos();

            List<DocumentInfo> infos = documentInfos.getDocument();
            for (Document document : bag) {
                DocumentInfo bInfo = documentToDocumentInfo(document);
                infos.add(bInfo);
            }

            MessageBodyWriter<DocumentInfos> p = providers.getMessageBodyWriter(DocumentInfos.class, aClass, annotations, mediaType);
            p.writeTo(documentInfos, DocumentInfos.class, DocumentInfos.class, annotations, mediaType, headers, outputStream);
        } else if (entity instanceof Note) {
            NoteInfo noteInfo = noteToNoteInfo((Note) entity);
            MessageBodyWriter<NoteInfo> p = providers.getMessageBodyWriter(NoteInfo.class, aClass, annotations, mediaType);
            p.writeTo(noteInfo, NoteInfo.class, NoteInfo.class, annotations, mediaType, headers, outputStream);
        } else if (entity instanceof NoteBag) {
            NoteBag bag = (NoteBag) entity;
            NoteInfos noteInfos = new NoteInfos();

            List<BNoteInfo> infos = noteInfos.getNote();
            for (Note note : bag) {
                BNoteInfo bInfo = noteToBNoteInfo(note);
                infos.add(bInfo);
            }

            MessageBodyWriter<NoteInfos> p = providers.getMessageBodyWriter(NoteInfos.class, aClass, annotations, mediaType);
            p.writeTo(noteInfos, NoteInfos.class, NoteInfos.class, annotations, mediaType, headers, outputStream);
        } else if (entity instanceof Firm) {
            FirmInfo firmInfo = firmToFirmInfo((Firm) entity);
            MessageBodyWriter<FirmInfo> p = providers.getMessageBodyWriter(FirmInfo.class, aClass, annotations, mediaType);
            p.writeTo(firmInfo, FirmInfo.class, FirmInfo.class, annotations, mediaType, headers, outputStream);
        } else if (entity instanceof FirmBag) {
            FirmBag bag = (FirmBag) entity;
            FirmInfos firmInfos = new FirmInfos();

            List<BFirmInfo> infos = firmInfos.getFirms();
            for (Firm firm : bag) {
                BFirmInfo bInfo = firmToBFirmInfo(firm);
                infos.add(bInfo);
            }

            MessageBodyWriter<FirmInfos> p = providers.getMessageBodyWriter(FirmInfos.class, aClass, annotations, mediaType);
            p.writeTo(firmInfos, FirmInfos.class, FirmInfos.class, annotations, mediaType, headers, outputStream);
        }
    }

    private BFirmInfo firmToBFirmInfo(Firm firm) {
        BFirmInfo bInfo = new BFirmInfo();
        bInfo.setFirmId(firm.getId());
        bInfo.setUrl(firm.getUrl());
        return bInfo;
    }

    private FirmInfo firmToFirmInfo(Firm firm) {
        FirmInfo fInfo = new FirmInfo();
        BFirmInfo bInfo = new BFirmInfo();
        bInfo.setUrl(firm.getUrl());
        fInfo.setFirm(bInfo);
        return fInfo;
    }

    private BNoteInfo noteToBNoteInfo(Note note) {
        BNoteInfo bInfo = new BNoteInfo();
        bInfo.setNoteId(note.getId());
        bInfo.setTimestamp(note.getTimestamp());
        bInfo.setNoteUri(note.getUri().toString());
        bInfo.setNote(note.getNote());
        return bInfo;
    }

    private DocumentInfo documentToDocumentInfo(Document d) {
        DocumentInfo dInfo = new DocumentInfo();
        dInfo.setDocumentId(d.getId());
        dInfo.setDocumentUri(d.getUri().toString());
        dInfo.setOriginalName(d.getOriginalName());
        dInfo.setMimetype(d.getMimeType());
        return dInfo;
    }

    private NoteInfo noteToNoteInfo(Note note) {
        NoteInfo nInfo = new NoteInfo();
        BNoteInfo bInfo = new BNoteInfo();
        bInfo.setNoteId(note.getId());
        bInfo.setTimestamp(note.getTimestamp());
        bInfo.setNoteUri(note.getUri().toString());
        bInfo.setNote(note.getNote());
        nInfo.setNote(bInfo);
        return nInfo;
    }

    private XDocumentInfo documentToXDocumentInfo(Document d) {
        XDocumentInfo xDInfo = new XDocumentInfo();
        DocumentInfo dInfo = documentToDocumentInfo(d);
        xDInfo.setDocument(dInfo);
        return xDInfo;
    }

    private ContactInfo contactToContactInfo(Contact contact) {
        ContactInfo cInfo = new ContactInfo();
        cInfo.setContact(contactToBContactInfo(contact));
        return cInfo;
    }

    private BContactInfo contactToBContactInfo(Contact contact) {
        BContactInfo bInfo = new BContactInfo();

        bInfo.setContactId(contact.getId());
        bInfo.setEmailAddress(contact.getEmailAddress());
        bInfo.setContactUri(uri(contact).toString());
        bInfo.setFirstName(contact.getFirstName());
        bInfo.setLastName(contact.getLastName());
        bInfo.setAddress1(contact.getAddress1());
        bInfo.setAddress2(contact.getAddress2());
        bInfo.setCity(contact.getCity());
        bInfo.setState(contact.getState());
        bInfo.setZip(contact.getZip());
        bInfo.setLandLine(contact.getLandLine());
        bInfo.setMobile(contact.getMobile());
        bInfo.setFax(contact.getFax());

        List<DocumentInfo> documentInfoList = bInfo.getDocumentInfo();
        Set<Document> docSet = contact.getDocument();
        for (Document d : docSet) {
            DocumentInfo dInfo = new DocumentInfo();
            dInfo.setDocumentId(d.getId());
            dInfo.setDocumentUri(d.getUri().toString());
            dInfo.setOriginalName(d.getOriginalName());
            dInfo.setMimetype(d.getMimeType());
            documentInfoList.add(dInfo);
        }

        List<BNoteInfo> bNoteInfoList = bInfo.getNoteInfo();

        Set<Note> notes = contact.getNote();
        for (Note note : notes) {
            BNoteInfo bNoteInfo = new BNoteInfo();
            bNoteInfo.setNoteId(note.getId());
            bNoteInfo.setNote(note.getNote());
            bNoteInfo.setNoteUri(note.getUri().toString());
            bNoteInfo.setTimestamp(note.getTimestamp());
            bNoteInfoList.add(bNoteInfo);
        }

        return bInfo;
    }


}
